<?php
/**
 * @todo cleanup the code (Оптимизировать повторяющиеся конструкции)
 * @author Kirill
 */
class Backend_Reports_Controller extends Backend_Controller
{
	/**
	 * @var Db_Query_Storage_Adapter_Abstract
	 */
	protected $_storage;
	/**
	 * @var Store_Session
	 */
	protected $_session;
	
	public function __construct()
	{
		/*
		 * Load classes by autoloader
		 * class should be loaded before unserializing
		 */
		class_exists('Db_Query');
		class_exists('Db_Query_Part');		
		
		parent::__construct();
		
		if($this->_configMain->get('use_cache')==='memcached')
			Db_Query_Storage::setCache(Application::dataCache());    
			
		$this->_storage = Db_Query_Storage::getInstance('file');	
		$this->_session = Store_Session::getInstance('Reports');
		
	}
	
	public function indexAction()
	{
		$res = Resource::getInstance();   
        $this->_resource->addInlineJs('
        	var canEdit = '.((boolean)$this->_user->canEdit($this->_module)).';
        	var canDelete = '.((boolean)$this->_user->canDelete($this->_module)).';
        '); 
	    $res->addJs('/js/app/system/FilesystemWindow.js', 1);
	    $res->addJs('/js/app/system/report/filter.js', 1);
	    $res->addJs('/js/app/system/crud/reports.js', 2);
	}
	/**
	 * Check if report is loaded
	 */
	public function checkloadedAction()
	{

		if($this->_session->keyExists('loaded') && $this->_session->get('loaded'))
		{
			$query = $this->_session->get('query');
			$rootPart = $query->getRootPart();
			$items = array();
			$partconfig = array();
			if($rootPart){
				$helper = Db_Object_Config::getInstance($rootPart->getObject());
				$partconfig['title'] = $helper->get('title');
				$partconfig['object'] = $rootPart->getObject();
				$items = $this->_partConfig($query , $rootPart);
			}		
			Response::jsonSuccess(array('items'=>$items,'partconfig'=>$partconfig));
		}
		else 
		{
			Response::jsonError();
		}
	}
	/**
	 * Load query into the session storage and return itemsconfig
	 */
	public function loadAction()
	{
		$file = Request::post('file', 'string', false);
		$path = $this->_configMain->get('report_configs');

		
		if(!$file || !file_exists($path.$file))
			Response::jsonError($this->_lang->WRONG_REQUEST);
					
		$queryId = $path.$file;			
		$query = $this->_storage->load($queryId);
		
		$this->_session->set('loaded' , true);
		$this->_session->set('query' , $query);
		$this->_session->set('file' , $queryId);
		
		Response::jsonSuccess();
	}
	/**
	 * Clear report
	 */
	public function clearAction(){
		
		if(!User_Admin::getInstance()->canEdit($this->_module))
			Response::jsonError($this->_lang->CANT_MODIFY);	
			
		$this->_checkLoaded();
		
		$query = new Db_Query();
		$this->_session->set('query' , $query);

		Response::jsonSuccess();
	}
	/**
	 * Load Query part fields config
	 */
	public function loadpartAction()
	{
		$parentPart= Request::post('parentpart', 'string', '');
		$parentField= Request::post('parentfield', 'string', '');
		$object = Request::post('object', 'string', false);
			
		if(!$object)
			Response::jsonError($this->_lang->WRONG_REQUEST . ' 1');
			
		try{
			$objectCfg = Db_Object_Config::getInstance($object);
		}catch (Exception $e){
			Response::jsonError($this->_lang->WRONG_REQUEST .' 2');
		}
		
		
		$this->_checkLoaded();
		
		
		$query = $this->_session->get('query');
		$part = $query->findChild($parentPart, $parentField);
		
		if(!$part)
			Response::jsonError($this->_lang->WRONG_REQUEST.' 3');
			
		Response::jsonSuccess($this->_partConfig($query , $part));
	
	}
	/**
	 * Remove part from report
	 */
	public function deselectsubAction(){
		$parentPart= Request::post('parentpart', 'string', '');
		$parentField= Request::post('parentfield', 'string', '');
		$object = Request::post('object', 'string', false);
			
		if(!$object)
			Response::jsonError($this->_lang->WRONG_REQUEST . ' code 1');
			
		if(!User_Admin::getInstance()->canEdit($this->_module))
			Response::jsonError($this->_lang->CANT_MODIFY);	
	
		$this->_checkLoaded();
			
		$query = $this->_session->get('query');	
		
		$query->getPart($parentPart)->setFieldCfg($parentField,'selectSub',false);
		
		if($query->removePart($query->findChild($parentPart, $parentField)->getId())){
			$this->_session->set('query' , $query);	
			Response::jsonSuccess();
			
		}else{
			Response::jsonError($this->_lang->CANT_EXEC);
		}
	}
	/**
	 * Set Join type for report part
	 */
	public function setjoinAction(){
		$parentPart= Request::post('parentpart', 'string', '');
		$parentField= Request::post('parentfield', 'string', '');
		$object = Request::post('object', 'string', false);
		$joinType = Request::post('jointype', 'integer', 1);
			
		if(!$object)
			Response::jsonError($this->_lang->WRONG_REQUEST . ' code 1');
			
		if(!User_Admin::getInstance()->canEdit($this->_module))
			Response::jsonError($this->_lang->CANT_MODIFY);	

		$this->_checkLoaded();
			
		$query = $this->_session->get('query');	
		/**
		 * @var Db_Query_Part 
		 */
		$part = $query->getPart(Db_Query_Part::findId($parentPart, $parentField, $object));
		if(!$part)
			Response::jsonError($this->_lang->WRONG_REQUES);
			
		$part->setJoinType($joinType);		
		$this->_session->set('query' , $query);	
		Response::jsonSuccess();	
	}
	
	/**
	 * Save field config
	 */
	public function savefieldAction()
	{
		if(!User_Admin::getInstance()->canEdit($this->_module))
			Response::jsonError($this->_lang->CANT_MODIFY);
		
		$part = Request::post('part', 'string', false);
		$fieldOption = Request::post('fieldoption', 'string', '');
		$field = Request::post('field', 'string', '');
		$value = Request::post('value', 'string', '');
		
		$accepted = array('select','title','alias');
		
		if(!$part || !$field || !in_array($fieldOption, $accepted,true))
			Response::jsonError($this->_lang->WRONG_REQUEST .' 1');
		
		if($fieldOption === 'select')
			$value = Filter::filterValue('boolean', $value);
					

		$this->_checkLoaded();

		$query = $this->_session->get('query');	
		$part = $query->getPart($part);
		
		if($part === false)
			Response::jsonError($this->_lang->WRONG_REQUEST.' 3');	
				
		$part->setFieldCfg($field, $fieldOption, $value);	
		$this->_session->set('query' , $query);

		Response::jsonSuccess();	
	}
	/**
	 * Save report
	 */
	public function saveAction()
	{
		
		$this->_checkLoaded();
			
		if(!User_Admin::getInstance()->canEdit($this->_module))
			Response::jsonError($this->_lang->CANT_MODIFY);
			
		if($this->_storage->save($this->_session->get('file'), $this->_session->get('query')))
			Response::jsonSuccess();
		else 
			Response::jsonError($this->_lang->CANT_WRITE_FS);		
	}
	/**
	 * Load query SQL
	 */
	public function loadsqlAction()
	{
		$this->_checkLoaded();

		$query = $this->_session->get('query');
		
		Response::jsonSuccess(self::simpleFormatSql((string)$query->getSql()));
	}
    /**
     * Simple SQL format method, can return invalid formating in field values
     * @param string $query
     * @return string
     */
    static public function simpleFormatSql($query)
    {
    	$newLines = array(' from ',' where ',' limit ',' order by ' ,' left ' ,' right ', ' inner ' , ' join ');
    	$newLines2 = array('<br> FROM ','<br> WHERE ','<br> LIMIT ','<br> ORDER BY ' ,'<br> LEFT ' ,'<br> RIGHT  ', '<br> INNER ', ' JOIN ');
    	
    	$str = str_ireplace($newLines, $newLines2, $query);
 
    	return $str;
    }
	/**
	 * Add Query Part
	 */
	public function addpartAction()
	{
		$partId = Request::post('partid', 'string', false);
		$objectField = Request::post('objectfield', 'string', false);
		$subObject = Request::post('subobject', 'string', false);
	
		if(!$partId || !$objectField)
			Response::jsonError($this->_lang->WRONG_REQUEST);
			

		$this->_checkLoaded();

		$query = $this->_session->get('query');	

		$part = $query->getPart($partId);
		
		if($part === false)
			Response::jsonError($this->_lang->WRONG_REQUEST .' code 3');
		
		$oCfg = Db_Object_Config::getInstance($part->getObject());
		//$subObject = $oCfg->getLinkedObject($objectField);
		
		
		$childPart = $query->findChild($partId , $objectField);
		if($childPart)
			$query->removePart($childPart->getId());
		
		$newPart = new Db_Query_Part();
		$newPart->setObject($subObject);
		$newPart->setParentField($objectField);
		
		if(!$query->addPart($newPart , $partId))
			Response::jsonArray(array('success'=>true,'msg'=>$this->_lang->REPORT_RECURSION_LIMIT,'limit'=>true));
		
		$part->setFieldCfg($objectField, 'selectSub', true);	
			
	    $this->_session->set('query' , $query);
	    
	    Response::jsonSuccess();		
	}
	
	/**
	 * Get object fields
	 */
	public function objectfieldsAction()
	{
		$this->_checkLoaded();

		/**
		 * @var Db_Query
		 */	
		$query = $this->_session->get('query');	
			
		$object = Request::post('object', 'string', '0');
		
		if($object === '0'){
			$object = $query->getRootPart()->getObject();
		}
		
		try{
			$config = Db_Object_Config::getInstance($object);
		}catch(Exception $e){
			Response::jsonError($this->_lang->WRONG_REQUEST);
		}
		
		$fields = $config->getFieldsConfig(true);
		$result = array();

		foreach ($fields as $name=>$cfg)
			$result[] = array('id'=>$name,'title'=>$cfg['title']);
		
		
		Response::jsonSuccess($result);
	}

	protected function _partConfig(Db_Query $query , Db_Query_Part $part)
	{	
		$cfg = array();
		$fields = $part->getFields();
		$objectConfig = Db_Object_Config::getInstance($part->getObject());
		
		
		$mainPanel = new stdClass();
		$mainPanel->xtype='panel';
		$mainPanel->border = false;
		//$mainPanel->title = $objectConfig->get('title');

		ksort($fields);
		
		foreach ($fields as $name=>$config)
		{			
			$obj = new stdClass();
			$obj->xtype='reportfield';
			$obj->valueSelected=$config['select'];
			$obj->valueTitle = $config['title'];
			$obj->valueAlias = $config['alias'];
			$obj->valueField = $name;
			$obj->valueIsLink= $config['isLink'];
			$obj->valueSelectSub = $config['selectSub'];
			$obj->valueObject = $part->getObject();
			$obj->valuePartId = $part->getId();
			
			$obj->valueSubObject='';
			$obj->valueSubObjectTtile='';
			
			if($config['isLink']){
				$child = $query->findChild($part->getId(), $name);
				if($child!==false)
					$linked = $child->getObject();
				else
					$linked = $objectConfig->getLinkedObject($name);	
						
				$obj->valueSubObject = $linked;
				$obj->valueSubObjectTtile = Db_Object_Config::getInstance($linked)->get('title');
			}

			$cfg[] = $obj;
		}
		
		$mainPanel->items = $cfg;
		
		return array(
			'items'=>$mainPanel,
			'objectcfg'=>array(
				'join'=>$part->joinType,
				'title'=>$objectConfig->get('title'),
				'object'=>$part->getObject()
			)
		);
	}
	/**
	 * Files list
	 */
	public function fslistAction()
	{
		$path = Request::post('node', 'string', '');
		$path = str_replace('.','', $path);
			
		$dirPath = $this->_configMain->get('report_configs');
				
		if(!is_dir($dirPath))
			Response::jsonArray(array());
			
		$files = File::scanFiles($this->_configMain->get('report_configs') . $path, array('.dat'),false,File::Files_Dirs);
		
		if(empty($files))
			Response::jsonArray(array());
			
		$list = array();

		foreach($files as $k=>$fpath)
		{
			$obj = new stdClass();
			$obj->id =str_replace($dirPath, '', $fpath);
			$obj->text = basename($fpath);
			
			if(is_dir($fpath))
			{
				$obj->expanded = false;
				$obj->leaf = false;
			} 
			else
			{
				$obj->leaf = true;
			}
			$list[] = $obj;	
		}	
		Response::jsonArray($list);	
	}
	/**
	 * Create config subfolder
	 */
	public function fsmakedirAction()
	{
		if(!User_Admin::getInstance()->canEdit($this->_module))
			Response::jsonError($this->_lang->CANT_MODIFY);
		
		$name = Request::post('name', 'string', '');
		$path = Request::post('path', 'string', '');
		
		$name = str_replace(array(DIRECTORY_SEPARATOR), '' , $name);
		
		if(!strlen($name))
			Response::jsonError($this->_lang->WRONG_REQUEST . ' [code 1]');
		
		$newPath = $this->_configMain->get('report_configs');
		
		if(strlen($path))
		{
			if(!is_dir($newPath. $path))
				Response::jsonError($this->_lang->WRONG_REQUEST  . ' [code 2]');
				
			$newPath.= $path . DIRECTORY_SEPARATOR;
		}
		 
		$newPath.= DIRECTORY_SEPARATOR . $name;			
			
		if(mkdir($newPath, 0775))
			Response::jsonSuccess();
		else
			Response::jsonError($this->_lang->CANT_WRITE_FS);	
	}
	/**
	 * Create new report
	 */
	public function fsmakefileAction()
	{
		if(!User_Admin::getInstance()->canEdit($this->_module))
			Response::jsonError($this->_lang->CANT_EDIT);
		
		$name = Request::post('name', 'string', '');
		$path = Request::post('path', 'string', '');
		
		$name = str_replace(array(DIRECTORY_SEPARATOR), '' , $name);
		
		if(!strlen($name))
			Response::jsonError($this->_lang->WRONG_REQUEST . ' [code 1]');
		
		$newPath = $this->_configMain->get('report_configs');

		if(strlen($path))
			$filepath = $newPath. $path . DIRECTORY_SEPARATOR . $name.'.report.dat';
		else 
			$filepath = $newPath . $name . '.report.dat';
		
		if(file_exists($filepath))
			Response::jsonError($this->_lang->FILE_EXISTS);
		 		
		$obj = new Db_Query();
		$storage =  Db_Query_Storage::getInstance('file');
		if($storage->save($filepath, $obj))
			Response::jsonSuccess(array('file'=>DIRECTORY_SEPARATOR . str_replace($newPath, '', $filepath)));
		else
			Response::jsonError($this->_lang->CANT_WRITE_FS);	
	}
	
	protected function _checkLoaded(){
		if(!$this->_session->keyExists('loaded') || !$this->_session->get('loaded'))
			Response::jsonError($this->_lang->MSG_REPORT_NOT_LOADED);
	}
	
	/**
	 * Get results grid config
	 */
	public function resultsAction()
	{	
		$this->_checkLoaded();
			
		$query = $this->_session->get('query');
		$fieldsCfg = $query->getSelectedColumns();
		
		if(empty($fieldsCfg))
			Response::jsonError($this->_lang->WRONG_REQUEST);
		
		$fields = array();
		$columns = array();
	
		foreach($fieldsCfg as $item)
		{
			$column = new stdClass();
			$field = new stdClass();
			
			$field->name = $item['name'];
			$field->type = 'text';
			
			$column->dataIndex = $item['name'];
			$column->header = $item['title'];
			$column->flex = 1;	

			$fields[] = $field;
			$columns[] = $column;	
		}
		Response::jsonSuccess(array('fields'=>$fields,'columns'=>$columns));
	}
	/**
	 * Get query results
	 */
	public function dataAction()
	{
		$pager = Request::post('pager', 'array', array());

		$this->_checkLoaded();
			
		$query = $this->_session->get('query');	
		/**
		 * @var Zend_Db_Select
		 */
		$sql = $query->getSql();
		
		if(!empty($pager))
			Model::queryAddPagerParams($sql, $pager);
		
		$data = $this->_db->fetchAll($sql);
		$count = $this->_db->fetchOne($query->getCountSql());
		
		Response::jsonSuccess($data,array('count'=>$count));		
	}
	/**
	 * Get Orm objects list
	 */
	public function objectsAction()
	{
		$cfgPath = $this->_store->get('config_main')->get('object_configs');          
        /*
         * Getting list of objects
         */
        $configs = File::scanFiles($cfgPath , array('.php'), false, File::Files_Only);
        if(empty($configs))
             Response::jsonSuccess(array());

        $data = array();     
        foreach($configs as $file){
        	$file = basename(substr($file,0,-4));
        	$conf = Db_Object_Config::getInstance($file);
        	$data[] = array('id'=>$file , 'title'=>$conf->get('title'));
        }      
        Response::jsonArray($data);
	}
	/**
	 * Add base object into report
	 */
	public function addbaseAction()
	{
		$this->_checkLoaded();
			
		if(!User_Admin::getInstance()->canEdit($this->_module))
			Response::jsonError($this->_lang->CANT_MODIFY);	
			
		$objectName = Request::post('object', 'string', false);

		/**
		 * @var Db_Query
		 */
		$query = $this->_session->get('query');
		
		$part = new Db_Query_Part();
		$part->setObject($objectName);
		$query->addPart($part);
		$this->_session->set('query',$query);
		Response::jsonSuccess();
	}
	/**
	 * Add related object selection
	 */
	public function addSubAction()
	{

		$this->_checkLoaded();
			
		if(!User_Admin::getInstance()->canEdit($this->_module))
			Response::jsonError($this->_lang->CANT_MODIFY);	
			
		$baseField = Request::post('basefield', 'string', false);
		$subObject = Request::post('subobject', 'string', false);
		$subField = Request::post('subfield', 'string', false);
		$join = Request::post('join', 'integer', 1);	
			
		if(!$baseField || !$subObject || !$subField || !$join)
			Response::jsonError($this->_lang->WRONG_REQUEST);
		
		$query = $this->_session->get('query');
		
		$basePart = $query->getRootPart();
		
		//$newId = Db_Query_Part::findId($basePart->getId(), $baseField, $subObject);
		$childPart = $query->findChild($basePart->getId(), $baseField);

		if($childPart!==false)
			$query->removePart($childPart->getId());
		
		$newPart = new Db_Query_Part();
		$newPart->setObject($subObject);
		$newPart->setParentField($baseField);
		$newPart->setJoinType($join);
				
		if(!$query->addPart($newPart , $basePart->getId()))
			Response::jsonArray(array('success'=>false,'msg'=>$this->_lang->CANT_EXEC));

		$basePart->setFieldCfg($baseField, 'selectSub', true);	
		$basePart->setFieldCfg($baseField, 'isLink', true);

	    $this->_session->set('query' , $query);
	    Response::jsonSuccess();		
	}
	
	/**
	 * Clear report session
	 */
	public function closeAction()
	{
		$this->_session->remove('loaded');
		$this->_session->remove('query');
		$this->_session->remove('file');
		Response::jsonSuccess();	
	}
	/**
	 * Load CSV file
	 */
	public function exportcsvAction()
	{
		$this->_checkLoaded();
		
		$query = $this->_session->get('query');	
		/**
		 * @var Zend_Db_Select
		 */
		$sql = $query->getSql();		
		$data = $this->_db->fetchAll($sql);
		
		$fieldsCfg = $query->getSelectedColumns();
		
		$titles = Utils::fetchCol('title', Utils::rekey('name', $fieldsCfg));
			
		$data = array_merge(array($titles) , $data);
		
		$csv =  Export::factory(Export::CSV, 'Export_Layout_Table_Csv', $data);
		$csv->setFileName('Report');
		$csv->stream();
			
	}
	
	/**
	 * Get list of resultset filters
	 */
	public function filterslistAction()
	{
		$this->_checkLoaded();	
		$query = $this->_session->get('query');
		$result = array();
		Response::jsonSuccess($result);
	}
	/**
	 * Get list of selected objects
	 */
	public function objectlistAction()
	{
		$this->_checkLoaded();
			
		$query = $this->_session->get('query');
		$result = array();
		
		$objects = $query->getSelectedObjects();
		if(!empty($objects))
			foreach ($objects as $object)
				$result[] = array('id'=>$object , 'title'=>Db_Object_Config::getInstance($object)->get('title'));
			
		Response::jsonSuccess($result);
	}
	/**
	 * Get object fields
	 */
	public function fieldlistAction()
	{
		$object = Request::post('object', 'string', false);
		if(!$object)
			Response::jsonSuccess(array());
			
		$this->_checkLoaded();
			
		$query = $this->_session->get('query');
		$result = array();
		
		$fields = Db_Object_Config::getInstance($object)->getFieldsConfig(true);
		if(!empty($fields))
			foreach ($fields as $code =>$config)
				$result[] = array('id'=>$code,'title'=>$config['title']);
		
		Response::jsonSuccess($result);
	}
	
	/**
	 * Remove Query condition
	 */
	public function removeconditionAction()
	{
		$this->_checkCanEdit();

		$this->_checkLoaded();

		$query = $this->_session->get('query');
		$id = Request::post('id','integer',false);
		if($id === false)
			Response::jsonError($this->_lang->WRONG_REQUEST);
			
		$query->removeCondition($id);	
		$this->_session->set('query' , $query);
	}
	/**
	 * Get list of SQL operators
	 */
	public function operatorlistAction()
	{
		$dictionary = Dictionary::getInstance('sqloperator')->getData();
		$result = array();
		foreach ($dictionary as $key=>$value)
			$result[] = array('id'=>$key,'title'=>$value);
			
		Response::jsonSuccess($result);	
	}
	/**
	 * Get list of Query conditions
	 */
	public function conditionslistAction()
	{
	
		$this->_checkLoaded();

		$query = $this->_session->get('query');
		
		$result = array();
		$conditions = $query->getConditions();
		
		if(!empty($conditions)){
			$dict = Dictionary::getInstance('sqloperator');
			foreach ($conditions as $id=>$object){
				$tmp = get_object_vars($object);
				if($dict->isValidKey($tmp['operator']))
					$tmp['operator'] = $dict->getValue($tmp['operator']);
				$tmp['id'] = $id;
				$result[] = $tmp;
			}
		}
		Response::jsonSuccess($result);
	}
	/**
	 * Save SQL condition
	 * @todo check incoming values
	 */
	public function saveconditionAction()
	{
			$this->_checkCanEdit();
			
			$this->_checkLoaded();

			$query = $this->_session->get('query');
			
			$id = Request::post('id','integer',false);
			$object = Request::post('object','string',false);
			$field= Request::post('field','string',false);
			$operator= Request::post('operator','string',false);
			$value = Request::post('value','string',false);
			$value2= Request::post('value2','string',false);

			$dbCondition = new Db_Query_Condition();
			$dbCondition->object = $object;
			$dbCondition->field = $field;
			$dbCondition->operator = $operator; 
			$dbCondition->value = $value;
			$dbCondition->value2 = $value2;
			
			if($id===false)
				$query->addCondition($dbCondition);
			else 
				$query->setCondition($id , $dbCondition);
				
			$this->_session->set('query' , $query);	
			Response::jsonSuccess();
	}
	/**
	 * Get Db_Query_Condition config
	 */
	public function loadconditionAction()
	{
			$this->_checkLoaded();

			$query = $this->_session->get('query');
			
			$id = Request::post('id','integer',false);
			
			if($id===false)
				Response::jsonError($this->_lang->WRONG_REQUEST . ' code 2');
					
			$condition = $query->getCondition($id);
			
			if($condition === false)
				Response::jsonError($this->_lang->WRONG_REQUEST . ' code 3');
			
			$data = get_object_vars($condition);
			$data['id'] = $id;
			Response::jsonSuccess($data);
	}
}